package com.wzcl.app.service.common;

import com.alibaba.fastjson.JSON;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.wzcl.app.config.MyWxPayConfig;
import com.wzcl.app.dao.model.Orders;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.utils.AESUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@Log4j2
@Service
public class WxPayService implements MyPayService {

    SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
    private static WXPay wxPay = null;

    @Resource
    private MyWxPayConfig wxPayConfig;

    @Resource
    private AmqpTemplate amqpTemplate;

    @Override
    public String appPay(Orders orders, BigDecimal money, String name, String ip) {
        Map<String, String> map = new LinkedHashMap<>();
        Map<String, String> returnMap = new LinkedHashMap<>();
        try {
            String total_fee = money.multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_DOWN).toString();
            map.put("appid", wxPayConfig.getAppID());//公众账号ID
            map.put("mch_id", wxPayConfig.getMchID());//商户号
            map.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
            map.put("body", "望子成龙网校-订单支付");//商品描述
            map.put("out_trade_no", orders.getNo());//商户订单号
            map.put("total_fee", total_fee);//支付金额，单位为分
            Date timeExpire = new Date(System.currentTimeMillis() + 15 * 60 * 1000L);
            map.put("time_expire", format.format(timeExpire));//过期时间
            map.put("spbill_create_ip", ip);    //自己的服务器IP地址
            map.put("notify_url", wxPayConfig.getNotifyUrl());//异步通知地址
            map.put("trade_type", "APP"); //交易类型JSAPI/Native/App
            String sign = WXPayUtil.generateSignature(map, wxPayConfig.getKey());
            map.put("sign", sign); //签名
            //使用官方API请求预付订单
            Map<String, String> response = getWxPay().unifiedOrder(map);
            log.info("统一订单{}", JSON.toJSONString(response));
            //获取返回码
            String returnCode = response.get("return_code");
            //返回给前端数据
            returnMap.put("appid", wxPayConfig.getAppID());
            returnMap.put("noncestr", response.get("nonce_str"));
            returnMap.put("package", "Sign=WXPay");
            returnMap.put("partnerid", wxPayConfig.getMchID());
            if (returnCode.equals("SUCCESS") && "SUCCESS".equals(response.get("result_code"))) {
                returnMap.put("prepayid", response.get("prepay_id"));
                returnMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
                returnMap.put("sign", WXPayUtil.generateSignature(returnMap, wxPayConfig.getKey()));
                return JSON.toJSONString(returnMap);
            } else {
                log.error("微信调用失败{}", response.get("return_msg"));
                return null;
            }
        } catch (Exception e) {
            log.error("微信支付错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL, e.getMessage());
        }
    }

    //TODO 支付记录哈哈哈
    /*@Override
    public String query(PayRecord record) {
        Map<String, String> map = new LinkedHashMap<>();
        Map<String, String> returnMap = new LinkedHashMap<>();
        try {
            map.put("appid", wxPayConfig.getAppID());//公众账号ID
            map.put("mch_id", wxPayConfig.getMchID());//商户号
            map.put("out_trade_no", record.getOrderNo());//商户订单号
            map.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
            String sign = WXPayUtil.generateSignature(map, wxPayConfig.getKey());
            map.put("sign", sign); //签名
            Map<String, String> response = getWxPay().orderQuery(map);
            log.info("订单查询{}", JSON.toJSONString(response));
            String returnCode = response.get("return_code");
            if (returnCode.equals("SUCCESS")) {
                //调用成功
                returnMap.put("nonce_str", response.get("nonce_str"));
                returnMap.put("mch_id", response.get("mch_id"));
                returnMap.put("appid", response.get("appid"));
                returnMap.put("sign", response.get("sign"));
                returnMap.put("result_code", response.get("result_code"));
                if ("SUCCESS".equals(returnMap.get("result_code"))) {
                    //统一下单成功
                    returnMap.put("trade_type", "trade_type");
                    returnMap.put("prepay_id", response.get("prepay_id"));
                    return JSON.toJSONString(returnMap);
                } else {
                    return JSON.toJSONString(returnMap);
                }
            } else {
                return JSON.toJSONString(returnMap);
            }
        } catch (Exception e) {
            log.error("微信支付查询错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL, e.getMessage());
        }
    }*/

    //TODO 支付记录哈哈哈
    /*@Override
    public void refund(PayRecord record) {
        Map<String, String> map = new LinkedHashMap<>();
        try {
            map.put("appid", wxPayConfig.getAppID());//公众账号ID
            map.put("mch_id", wxPayConfig.getMchID());//商户号
            map.put("transaction_id", record.getTradeNo());//商户订单号
            map.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
            map.put("out_refund_no", record.getOrderNo());//商户退款单号
            String pay = record.getPay().multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_DOWN).toString();
            map.put("total_fee", pay);
            map.put("refund_fee", pay);
            map.put("notify_url", wxPayConfig.getRefundNotifyUrl());
            String sign = WXPayUtil.generateSignature(map, wxPayConfig.getKey());
            map.put("sign", sign); //签名
            Map<String, String> response = getWxPay().refund(map);
            log.info("订单退款{}", JSON.toJSONString(response));
            String returnCode = response.get("return_code");
            if ("SUCCESS".equals(returnCode)) {
                //调用成功
                if ("SUCCESS".equals(response.get("result_code"))) {
                    //退款申请接收成功
                } else {
                    log.error("微信退款申请失败code{},codeMsg{}",response.get("err_code"),response.get("err_code_des"));
                    throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL);
                }
            } else {
                log.error("微信退款调用失败{}",response.get("return_msg"));
                throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL);
            }
        } catch (BusinessException e){
            throw e;
        }
        catch (Exception e) {
            log.error(" 微信退款错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL, e.getMessage());
        }
    }*/

    //TODO 支付记录哈哈哈
    /*@Override
    public String queryRefund(PayRecord record) {
        Map<String, String> map = new LinkedHashMap<>();
        Map<String, String> returnMap = new LinkedHashMap<>();
        try {
            map.put("appid", wxPayConfig.getAppID());//公众账号ID
            map.put("mch_id", wxPayConfig.getMchID());//商户号
            map.put("nonce_str", WXPayUtil.generateNonceStr());//随机字符串
            map.put("refund_id", record.getOrderNo());//商户订单号
            String sign = WXPayUtil.generateSignature(map, wxPayConfig.getKey());
            map.put("sign", sign); //签名
            Map<String, String> response = getWxPay().refundQuery(map);
            log.info("退款查询{}", JSON.toJSONString(response));
            String returnCode = response.get("return_code");
            if (returnCode.equals("SUCCESS")) {
                //调用成功
                returnMap.put("result_code", response.get("result_code"));
                returnMap.put("nonce_str", response.get("nonce_str"));
                returnMap.put("mch_id", response.get("mch_id"));
                returnMap.put("appid", response.get("appid"));
                returnMap.put("transaction_id", response.get("transaction_id"));
                returnMap.put("out_trade_no", response.get("out_trade_no"));
                returnMap.put("total_fee", response.get("total_fee"));
                returnMap.put("sign", response.get("sign"));
                if ("SUCCESS".equals(returnMap.get("resultCode"))) {
                    //退款申请接收成功
                }
                return JSON.toJSONString(returnMap);
            } else {
                return JSON.toJSONString(returnMap);
            }
        } catch (Exception e) {
            log.error("微信查询退款错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
            throw new BusinessException(CodeMsg.PAY_OPERATE_FAIL, e.getMessage());
        }
    }*/

    @Override
    public void notify(HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlBack = null;
            String notifyData = requestToString(request);
            log.info("微信支付回调：{}", notifyData);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);// 调用官方SDK转换成map类型数据
            if (check(notifyMap)) {//验证签名是否有效，有效则进一步处理
                String return_code = notifyMap.get("return_code");//状态
                if (return_code.equals("SUCCESS")) {
                    //TODO 支付记录哈哈哈
                    /*PayRecord payRecord = new PayRecord();
                    payRecord.setPayType(WZCL.PAY_TYPE.WX_PAY);
                    payRecord.setOrderNo(notifyMap.get("out_trade_no"));
                    payRecord.setTradeNo(notifyMap.get("transaction_id"));
                    if ("SUCCESS".equals(notifyMap.get("result_code"))) {
                        payRecord.setPay(new BigDecimal(notifyMap.get("total_fee")).divide(new BigDecimal("100")));
                        payRecord.setPayTradeAccount(notifyMap.get("openid"));
                        payRecord.setPayTime(format.parse(notifyMap.get("time_end")));
                        payRecord.setPayState(WZCL.PAY_STATE.PAID);
                        payRecord.setSupportRefund(true);
                    } else {
                        payRecord.setPayState(WZCL.PAY_STATE.PAY_FAIL);
                        log.error("订单{}支付失败,微信订单:{}.err_code:{},err_code_des:{}",
                                notifyMap.get("out_trade_no"),
                                notifyMap.get("transaction_id"),
                                notifyMap.get("err_code"),
                                notifyMap.get("err_code_des"));
                    }*/
                    //TODO 支付记录哈哈哈
                    //amqpTemplate.convertAndSend(RabbitMqConfiguration.DIRECT_QUEUE_PAID, JSON.toJSONString(payRecord));
                    xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                } else {
                    String msg = notifyMap.get("return_msg");
                    msg = StringUtils.isBlank(msg)?"报文为空":msg;
                    log.error("微信支付回调失败{}",msg);
                    xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA["+msg+"]]></return_msg>" + "</xml> ";
                }
                PrintWriter writer = response.getWriter();
                writer.write(xmlBack);
                writer.flush();
                writer.close();
            } else {
                log.error("微信支付回调通知签名错误");
            }
        } catch (Exception e) {
            log.error("微信支付回调处理错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
        }
    }

    @Override
    public void refundNotify(HttpServletRequest request, HttpServletResponse response) {

        try {
            String xmlBack = null;
            String notify = requestToString(request);
            log.info("微信退款回调:{}", notify);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notify);// 调用官方SDK转换成map类型数据
            if (StringUtils.equals(wxPayConfig.getAppID(),notifyMap.get("appid"))){//验证签名是否有效，有效则进一步处理
                String return_code = notifyMap.get("return_code");//状态
                if (return_code.equals("SUCCESS")) {
                    String req_info = AESUtil.decryptData(notifyMap.get("req_info"), wxPayConfig.getKey());
                    log.info("退款回调解密:{}",req_info);
                    Map<String, String> info = readStringXmlOut(req_info);
                    if ("SUCCESS".equals(info.get("refund_status"))) {
                        //TODO 支付记录哈哈哈
                        /*PayRecord payRecord = new PayRecord();
                        payRecord.setOrderNo(info.get("out_trade_no"));
                        payRecord.setPayState(WZCL.PAY_STATE.REFUNDED);//已退款
                        payRecord.setRefundTradeNo(info.get("refund_id"));//退款Id*/
                        //TODO 支付记录哈哈哈
                        //amqpTemplate.convertAndSend(RabbitMqConfiguration.DIRECT_QUEUE_PAID, JSON.toJSONString(payRecord));
                    } else {
                        log.error("微信退款失败:{}",info.get("refund_status"));
                    }
                    xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                } else {
                    String msg = notifyMap.get("return_msg");
                    msg = StringUtils.isBlank(msg)?"报文为空":msg;
                    log.error("微信退款回调失败{}",msg);
                    xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA["+msg+"]]></return_msg>" + "</xml> ";
                }
                PrintWriter writer = response.getWriter();
                writer.write(xmlBack);
                writer.flush();
                writer.close();
            } else {
                log.error("非手机退款回调通知错误");
            }
        } catch (Exception e) {
            log.error("微信支付回调通知失败:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
        }
    }

    @Override
    public boolean check(Map<String, String> params) throws Exception {
        return getWxPay().isPayResultNotifySignatureValid(params);
    }


    private WXPay getWxPay() {
        if (wxPay == null) {
            wxPay = new WXPay(wxPayConfig);
        }
        return wxPay;
    }

    public static String requestToString(HttpServletRequest request) throws IOException {
        InputStream is = request.getInputStream();
        //将InputStream转换成String
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
        is.close();
        return sb.toString();
    }

    public static Map<String, String> readStringXmlOut(String xml) {
        Map<String, String> map = new HashMap<String, String>();
        Document doc = null;
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            List<Element> list = rootElt.elements();// 获取根节点下所有节点
            for (Element element : list) { // 遍历节点
                map.put(element.getName(), element.getText()); // 节点的name为map的key，text为map的value
            }
        }catch (Exception e) {
            log.error("XML解析错误:{} \r\n 堆栈信息:{}",e.getMessage(), BusinessException.collectExceptionStackMsg(e));
        }
        return map;
    }

}

